/**
 * David Schneider
 * CSE 431
 * Homework 1
 * 
 * 
 */


import hw1.*;

import java.util.Enumeration;

/***
 * 
 * 
 * @author David Schneider
 * Summary:
 * The FSA class defines a finite state machine to convert a cup file into a set of edges.
 * 
 * Specifics:
 * The state machine has 12 states and 12 actions. 
 * To eliminate the problem of keywords being used as symbols, ambiguous keyword or string 
 * references perform the actions of both a keyword or a string and when more tokens reveal 
 * the type, the state machine performs the correct actions.
 * 
 */

public class Fsa {

	//A set of functions to quickly toggle debug output
	public static final boolean DEBUG = false;
	public static void debug(String out){
		if(DEBUG)
			System.out.println(out);
	}
	
	//Specify a set of constant actions numbers to avoid confusion
	public static final int NTH = 0,//Do nothing
							RNT = 1,//Raise non-terminal flag
							STP = 2,//Set type
							ASM = 3,//Add symbol
							SST = 4,//Set start
							SDS = 5,//Set define symbol
							STR = 6,//Set terminal field
							SNT = 7,//Set nonterminal field
							DEF = 8,//Define a symbol
							DFR = 9,//Define a symbol and reset
							RST = 10,//Reset values
							E   = 11;//Abort on E  or of invalid input

	static int GOTO[][] = {
		/*     B    D    S    O    C    S    T    N    S    W    O */
		/*     l    e    e    r    o    t    e    o    t    i    t */
		/*     a    f    m         m    r    r    n    a    t    h */
		/*     n    i    i         m         m         r    h    e */
		/*     k    n              a         i         t         r */
		/*          e                        n                     */
		/*                                   a                     */
		/*                                   l                     */
		/*                                                         */
		{      0,   0,   0,   0,   0,   8,   2,   1,   5,   8,   0} /* 0 */,
		{      1,   9,   0,   0,   0,   0,   2,   0,   0,   0,   0} /* 1 */,
		{      2,   9,   0,   0,   0,   3,   0,   0,   0,   0,   0} /* 2 */,
		{      3,   0,   0,   0,   0,   4,   4,   4,   4,   4,   0} /* 3 */,
		{      4,   0,/**/0,  0,   3,   0,   0,   0,   0,   0,   0} /* 4 */,
		{      5,   9,   0,   0,   0,   0,   0,   0,   0,   6,   0} /* 5 */,
		{      6,   0,   0,   0,   0,   7,   7,   7,   7,   7,   0} /* 6 */,
		{      7,   0,/**/0,  0,   0,   0,   0,   0,   0,   0,   0} /* 7 */,
		{      8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0} /* 8 */,
		{      9,   0,   0,   0,   0,  10,  10,  10,  10,  10,   0} /* 9 */,
		{     10,   0,/**/0,  9,   0,  11,  11,  11,  11,  11,   0} /* 10 */,
		{     11,   0,/**/0,  9,   0,   0,   0,   0,   0,   0,   0} /* 11 */,
	};

	static int ACTION[][] = {
		/*		B		D		S		O		C		S		T		N		S		W		O */
		/*		l		e		e		r		o		t		e		o		t		i		t */
		/*		a		f		m				m		r		r		n		a		t		h */
		/*		n		i		i				m				m				r		h		e */
		/*		k		n						a				i				t				r */
		/*				e										n								  */
		/*														a								  */
		/*														l								  */
		/*			                                                   							  */
		{		NTH, 	E  ,	E  ,	E  ,	E  ,	SDS,	SDS,	RNT,	SDS,	SDS,	E  } /* 0 */,
		{		NTH,	NTH,	E  ,	E  ,	E  ,	E  ,	NTH,	E  ,	E  ,	E  ,	E  } /* 1 */,
		{		NTH,	NTH,	E  ,	E  ,	E  ,	NTH,	E  ,	E  ,	E  ,	E  ,	E  } /* 2 */,
		{		NTH,	E  ,	E  ,	E  ,	E  ,	ASM,	ASM,	ASM,	ASM,	ASM,	E  } /* 3 */,
		{		NTH,	E  ,	RST,	E  ,	NTH,	E  ,	E  ,	E  ,	E  ,	E  ,	E  } /* 4 */,
		{		NTH,	NTH,	E  ,	E  ,	E  ,	E  ,	E  ,	E  ,	E  ,	NTH,	E  } /* 5 */,
		{		NTH,	E  ,	E  ,	E  ,	E  ,	SST,	SST,	SST,	SST,	SST,	E  } /* 6 */,
		{		NTH,	E  ,	NTH,	E  ,	E  ,	E  ,	E  ,	E  ,	E  ,	E  ,	E  } /* 7 */,
		{		NTH,	NTH,	E  ,	E  ,	E  ,	E  ,	E  ,	E  ,	E  ,	E  ,	E  } /* 8 */,
		{		NTH,	E  ,	E  ,	E  ,	E  ,	STR,	STR,	STR,	STR,	STR,	E  } /* 9 */,
		{		NTH,	E  ,	DFR,	DEF,	E  ,	SNT,	SNT,	SNT,	SNT,	SNT,	E  } /* 10 */,
		{		NTH,	E  ,	DFR,	DEF,	E  ,	E  ,	E  ,	E  ,	E  ,	E  ,	E  } /* 11 */,
	};
	
	/**
	 * Variables for holding temporary data of the finite state machine
	 * These variables were moved outside of the constructor so helper functions can access them and for style
	 */
	private String lhs, term, nonterm, type, start;
	private boolean terminal, lhsDeclared;
	private Token t;
	private SymbolTable symboltable;
	private int state;
	
	/**
	 * Resets all the private variables to their default state
	 * Should always be called from whenever returning to base state (0) from a different state that alters the private variables
	 */
	private void reset(){
		lhs     = "";
		term    = ""; 
		nonterm = "$FINAL$";
		type	= "";
		terminal = true;
		lhsDeclared = false;
	}
	
		/**
		 * Defines a new symbol
		 * Additionally will declare symbols if they are currently undeclared
		 */
	private void defineSymbol(){
			//Declare lhs if it is undeclared
		if(!symboltable.inTable(lhs)){
			symboltable.enterNonTerminal(lhs);
		}
			//Declare nonterm if it is undeclared
		if(!nonterm.equals("$FINAL$") && !symboltable.inTable(nonterm)){
			symboltable.enterNonTerminal(nonterm);
		}
			//Declare term if it is undeclared
		if(!symboltable.inTable(term)){
			symboltable.enterTerminal(term);
		}
		System.out.println("Edge "+lhs+" "+nonterm+" "+term);
	
		//Reset only the second right side in case of finding an or
		term = "";
		nonterm = "$FINAL$";
	}
	
	/**
	 * Sets the lhs variable
	 * Previously this function did more things, it is remains as a hook if more actions are needed
	 */
	private void setLhs(){
		lhs = t.strValue();
	}
	
	
	public Fsa(Enumeration e) {
		// Uncomment the line below and each token processed will be echoed.
		// ((TokenEnum)e).setDebug(true);

		symboltable = new SymbolTable();
		state = 0;
		reset();
		t = null;
		
		

		while (e.hasMoreElements()) {
			t = (Token)e.nextElement();

			debug("Got token " + t.type() + " with value " + t.strValue());

			int action = ACTION[state][t.type()];
			int newstate = GOTO[state][t.type()];

			debug("State " + state +
					" Performing action " + action + " and going to " + newstate);
			
			
			
			switch (action) {
			case NTH://Do nothing
				break;
				
			case RNT://Raise non-terminal flag
				setLhs();
				terminal = false;
				break;
				
			case STP://Set type value
				type = t.strValue();
				break;
				
			case ASM://Add symbol
				if(terminal){
					symboltable.enterTerminal(t.strValue());
				}
				else{
					symboltable.enterNonTerminal(t.strValue());
				}
				break;
				
			case SST://Set start
				if(start == null || start.equals("")){
					if(!symboltable.inTable(t.strValue())){
						symboltable.enterNonTerminal(t.strValue());
					}
					start = t.strValue();
					System.out.println("Start "+start);
				}
				else{
					oops("Start specified more than once: " + start + " and " + t.strValue());
				}
				break;
				
			case SDS://Set define symbol
				setLhs();
				break;
				
			case STR://Set terminal reference
				if(!symboltable.inTable(t.strValue())){
					symboltable.enterTerminal(t.strValue());
				}
				term = t.strValue();
				break;
				
			case SNT://Set non-terminal reference
				if(!symboltable.inTable(t.strValue())){
					symboltable.enterNonTerminal(t.strValue());
				}
				nonterm = t.strValue();
				break;
			
			case DEF://Define a symbol
				defineSymbol();
				break;
			
			case DFR://Define a symbol and reset
				defineSymbol();
				reset();
				break;
			
			case RST://Reset
				reset();
				break;
				
			case E: //Error action
				oops("Invalid input recieved");
				break;
			}

			state = newstate;
		}
		if (state != 0) oops("End in bad state: " + state);
	}

	void oops(String s) {
		System.err.println("Error: " + s);
		System.out.println("ABORT");
		System.exit(-1);
	}
}
